home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Utilities / StrHshTb.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  18.2 KB  |  678 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StrHshTb.cpp
  3.  
  4.     Contains:    xxx put contents here xxx
  5.  
  6.     Owned by:    Jens Alfke
  7.  
  8.     Copyright:    © 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <2>     5/24/96    jpa        1339104: Speed optimizations (mostly for
  13.                                     EditorSetup)
  14.  
  15.     To Do:
  16. */
  17.  
  18. /*
  19.     File:        StrHshTb.cpp
  20.  
  21.     Contains:    Implementation for StringHashTable class.
  22.  
  23.     Owned by:    Nick Pilch
  24.  
  25.     Copyright:    © 1993 - 1995 by Apple Computer, Inc., all rights reserved.
  26.  
  27.     In Progress:
  28.         
  29. */
  30.  
  31. /*
  32.     A simple hash table with chaining. Each hash table entry
  33.     is a HashEntryRec. Each HashEntryRec contains a pointer to start of a
  34.     linked list of EntryNodes. Each EntryNode contains the key, the length of
  35.     key, the value, the length of the value and at least
  36.     one link to another item in the list.
  37.     
  38.     The keys are stored as NULL terminated C-strings for debugging purposes
  39.     even though they may have imbedded NULLs.
  40. */
  41.  
  42. #ifndef _STRHSHTB_
  43. #include "StrHshTb.h"
  44. #endif
  45.  
  46. #ifndef __CTYPE__
  47. #include <ctype.h>
  48. #endif
  49.  
  50. #ifndef __LIMITS__
  51. #include <limits.h>
  52. #endif
  53.  
  54. #ifndef _EXCEPT_
  55. #include "Except.h"
  56. #endif
  57.  
  58. #ifndef __STRING__
  59. #include <string.h>
  60. #endif
  61.  
  62. #ifndef _ODNEW_
  63. #include "ODNew.h"
  64. #endif
  65.  
  66. #ifndef _UTILERRS_
  67. #include "UtilErrs.h"
  68. #endif
  69.  
  70. #pragma segment StringHashTable
  71.  
  72. //==============================================================================
  73. // Local Classes
  74. //==============================================================================
  75.  
  76. struct EntryNode
  77. {
  78.     ODUByte*    key;
  79.     ODULong    keySize;
  80.     ODPtr        value;
  81.     ODULong    valueLen;
  82.     EntryNode*    nextEntry; // == 0 means end of list.
  83. };
  84.  
  85. struct HashEntryRec
  86. {
  87.     EntryNode*    node;        //    pointer to first node
  88. };
  89.  
  90. #if 0
  91.     //OBSOLETE
  92.     struct LinkedNodesIterator
  93.     {
  94.         LinkedNodesIterator(StringHashTable* table, ODULong tableIndex)
  95.                             {fTableIndex = tableIndex; fTable = table;
  96.                                 fCurNode = (table->fTable + tableIndex)->node;}
  97.         EntryNode*    First() {return fCurNode;}
  98.         EntryNode*    Next() {return (fCurNode = fCurNode->nextEntry);}
  99.         ODBoolean    IsNotComplete() {return (fCurNode->nextEntry != 0);}
  100.       private:
  101.         StringHashTable*    fTable;
  102.         ODULong            fTableIndex;
  103.         EntryNode*            fCurNode;
  104.     };
  105. #endif // 0
  106.  
  107. struct LinkedNodesIterator
  108. {
  109.   public:
  110.     inline LinkedNodesIterator(StringHashTable* table, ODULong tableIndex);
  111.     inline    EntryNode*    First();
  112.     inline    EntryNode*    Next();
  113.     inline    void*/*ODBoolean*/    IsNotComplete();
  114.   private:
  115.     StringHashTable*    fTable;
  116.     ODULong            fTableIndex;
  117.     EntryNode*            fCurNode;
  118. };
  119.  
  120. LinkedNodesIterator::LinkedNodesIterator(StringHashTable* table, ODULong tableIndex)
  121. {
  122.     fTableIndex = tableIndex;
  123.     fTable = table;
  124.     fCurNode = (table->GetTable() + tableIndex)->node;
  125. }
  126.  
  127. EntryNode*    LinkedNodesIterator::First()
  128. {
  129.     return fCurNode;
  130. }
  131.  
  132. EntryNode*    LinkedNodesIterator::Next()
  133. {
  134.     return (fCurNode = fCurNode->nextEntry);
  135. }
  136.  
  137. void* /*ODBoolean*/    LinkedNodesIterator::IsNotComplete()
  138. {
  139.     return fCurNode/*!=kODNULL*/;
  140.     
  141.     /* Not comparing against NULL generates more efficient code in CW9. */
  142. }
  143.  
  144.  
  145. //==============================================================================
  146. // StringHashTable
  147. //==============================================================================
  148.  
  149. //------------------------------------------------------------------------------
  150. // StringHashTable::StringHashTable
  151. //------------------------------------------------------------------------------
  152.  
  153. StringHashTable::StringHashTable(ODULong numEntries)
  154. {
  155.     if (numEntries == 0)
  156.         fNumSlots = 1;
  157.     else
  158.         fNumSlots = numEntries;
  159.     fHashFunc = StringHashTable::StdHash;
  160.     fNumEntries = 0;
  161.     fHeapID = kDefaultHeapID;
  162. }
  163.  
  164. //------------------------------------------------------------------------------
  165. // StringHashTable::Initialize
  166. //------------------------------------------------------------------------------
  167.  
  168. void StringHashTable::Initialize(ODMemoryHeapID heapID)
  169. {
  170.     ODULong    tableSize = fNumSlots * sizeof(HashEntryRec);
  171.  
  172.     fHeapID = heapID;
  173.     fTable = (HashEntryRec*)ODNewPtrClear(tableSize, fHeapID);
  174.     if (!fTable)
  175.         THROW(kODErrOutOfMemory);
  176. }
  177.  
  178. //------------------------------------------------------------------------------
  179. // StringHashTable::~StringHashTable
  180. //------------------------------------------------------------------------------
  181.  
  182. StringHashTable::~StringHashTable()
  183. {
  184.     // gotta be careful to delete a node only after we're done with it.
  185.     for (ODULong i = 0; i < fNumSlots; i++)
  186.     {
  187.         LinkedNodesIterator    iter(this, i);
  188.         EntryNode*            someNode;
  189.         EntryNode*            lastNode = 0; // first time through
  190.  
  191.         for (someNode = iter.First();
  192.                 iter.IsNotComplete();
  193.                 someNode = iter.Next())
  194.         {
  195.             if (lastNode)
  196.             {
  197.                 ODDisposePtr((Ptr)lastNode->key);
  198.                 ODDisposePtr((Ptr)lastNode);
  199.             }
  200.             lastNode = someNode;
  201.         }
  202.         if (lastNode)
  203.         {
  204.             ODDisposePtr((Ptr)lastNode->key);
  205.             ODDisposePtr((Ptr)lastNode);
  206.         }
  207.     }
  208.     ODDisposePtr((Ptr)fTable);
  209. }
  210.  
  211. //------------------------------------------------------------------------------
  212. // StringHashTable::Insert
  213. //
  214. //    Hash the key, Map the hash to the range of table index values, insert the
  215. //    hash into the linked list of EntryNodes at that table slot.
  216. //------------------------------------------------------------------------------
  217.  
  218. void StringHashTable::Insert(ODUByte* string, ODULong stringLength,
  219.                                         ODPtr value, ODULong valueLength)
  220. {
  221.     if (stringLength == 0)
  222.         THROW(kODErrInvalidKey);
  223.     if (value == (ODPtr)kODNULL)
  224.         THROW(kODErrIllegalNullInput);
  225.     ODULong tableIndex = this->HashAndMap(string, stringLength);
  226.     this->InsertAtIndex(tableIndex, string, stringLength, value, valueLength);
  227.     ++fNumEntries;
  228. }
  229.  
  230. //------------------------------------------------------------------------------
  231. // StringHashTable::InsertAtIndex
  232. //
  233. //    Insert a hash table node at the the specified index in the table.
  234. //------------------------------------------------------------------------------
  235.  
  236. void StringHashTable::InsertAtIndex(ODULong index, ODUByte* string,
  237.                                                 ODULong stringLength,
  238.                                                 ODPtr value,
  239.                                                 ODULong valueLength)
  240. {
  241.     HashEntryRec*    entry = fTable + index;
  242.     
  243.     // No nodes at this table entry? Stick in the first one.
  244.     if (entry->node == kODNULL)
  245.         entry->node = this->MakeNewNode(string, stringLength, value,
  246.                                         valueLength);
  247.     // Else, need to follow chain to end checking for duplicate keys
  248.     else
  249.     {
  250.         LinkedNodesIterator    iter(this, index);
  251.         EntryNode*            someNode;
  252.         EntryNode*            lastNode;
  253.         ODBoolean            replaceOne = kODFalse;
  254.         ODBoolean            alreadyHadKey = kODFalse;
  255.  
  256.         // Iterate to the end of the list or until we find the key
  257.         for (someNode = iter.First();
  258.                 iter.IsNotComplete();
  259.                 lastNode = someNode, someNode = iter.Next())
  260.         {
  261.             if ((someNode->keySize == stringLength)
  262.                 && (!memcmp((const char*)someNode->key, (const char*)string,
  263.                             (size_t)stringLength)))
  264.             {
  265.                 someNode->value = value;
  266.                 someNode->valueLen = valueLength;
  267.                 alreadyHadKey = kODTrue;
  268.                 break;
  269.             }
  270.         }
  271.         if (!alreadyHadKey)
  272.             // If we got here, we have to create a new node.
  273.             lastNode->nextEntry = MakeNewNode(string, stringLength, value,
  274.                                                 valueLength);
  275.     }
  276. }
  277.  
  278. //------------------------------------------------------------------------------
  279. // StringHashTable::MakeStringFromBytes
  280. //
  281. //    Create a new NULL-terminated C-string from a stream of bytes.
  282. //------------------------------------------------------------------------------
  283.  
  284. ODUByte* StringHashTable::MakeStringFromBytes(ODUByte* string,
  285.                                                     ODULong stringLength)
  286. {
  287.     ODUByte* newString = (ODUByte*)ODNewPtr(stringLength + 1, fHeapID);
  288.     if (!newString)
  289.         THROW(kODErrOutOfMemory);
  290.     ODBlockMove(string, newString, stringLength);
  291.     newString[stringLength] = 0;
  292.     return newString;
  293. }
  294.  
  295. //------------------------------------------------------------------------------
  296. // StringHashTable::MakeNewNode
  297. //
  298. //    Allocate and intialize a new entry node.
  299. //------------------------------------------------------------------------------
  300.  
  301. EntryNode* StringHashTable::MakeNewNode(ODUByte* string,
  302.                                             ODULong stringLength, ODPtr value,
  303.                                             ODULong valueLength)
  304. {
  305.     EntryNode* newNode = (EntryNode*)ODNewPtr(sizeof(EntryNode), fHeapID);
  306.  
  307.     // Convert strings to C-strings and copy
  308.     ODUByte* newString = this->MakeStringFromBytes(string, stringLength);
  309.     
  310.     newNode->key = newString;
  311.     newNode->keySize = stringLength;
  312.     newNode->nextEntry = kODNULL;
  313.     newNode->value = value;
  314.     newNode->valueLen = valueLength;
  315.     
  316.     return newNode;
  317. }
  318.  
  319. //------------------------------------------------------------------------------
  320. // StringHashTable::Remove
  321. //
  322. //    Hash the key, Map the hash to the range of table index values, search the
  323. //    the linked list of EntryNodes for the key, remove the EntryNode in which
  324. //    the key was found.
  325. //------------------------------------------------------------------------------
  326.  
  327. void StringHashTable::Remove(ODUByte* string, ODULong stringLength)
  328. {
  329.     if (stringLength == 0)
  330.         THROW(kODErrInvalidKey);
  331.     ODULong tableIndex = this->HashAndMap(string, stringLength);
  332.     this->RemoveAtIndex(tableIndex, string, stringLength);
  333.     --fNumEntries;
  334. }
  335.  
  336. //------------------------------------------------------------------------------
  337. // StringHashTable::RemoveAtIndex
  338. //
  339. //    Remove a hash table node at the the specified index in the table.
  340. //------------------------------------------------------------------------------
  341.  
  342. void StringHashTable::RemoveAtIndex(ODULong index, ODUByte* string,
  343.                                                 ODULong stringLength)
  344. {
  345.     HashEntryRec*    entry = fTable + index;
  346.     
  347.     if (entry->node != kODNULL)
  348.     {
  349.         LinkedNodesIterator    iter(this, index);
  350.         EntryNode*            someNode;
  351.         EntryNode*            prevNode = 0; // start of chain
  352.  
  353.         for (someNode = iter.First();
  354.                 iter.IsNotComplete();
  355.                 someNode = iter.Next())
  356.         {
  357.             // look for a match
  358.             if ((someNode->keySize == stringLength)
  359.                 && (!memcmp((const char*)someNode->key, (const char*)string,
  360.                             (size_t)stringLength)))
  361.             {
  362.                 // Special case first node
  363.                 if (prevNode == 0)
  364.                     entry->node = someNode->nextEntry;
  365.                 else
  366.                     prevNode->nextEntry = someNode->nextEntry;
  367.  
  368.                 // Must dispose someNode->key first or else we'll lose its
  369.                 //    reference after disposing the someNode.
  370.                 ODDisposePtr((Ptr)someNode->key);
  371.                 ODDisposePtr((Ptr)someNode);
  372.                 break;
  373.             }
  374.             prevNode = someNode;
  375.         }
  376.     }
  377. }
  378.  
  379. //------------------------------------------------------------------------------
  380. // StringHashTable::Find
  381. //
  382. //    Hash the key, Map the hash to the range of table index values, search the
  383. //    the linked list of EntryNodes for the key. Signal error if not found.
  384. //------------------------------------------------------------------------------
  385.  
  386. ODBoolean StringHashTable::Find(ODUByte* string, ODULong stringLength,
  387.                                     ODPtr* value, ODULong* valueLength)
  388. {
  389.     ODULong        tableIndex = this->HashAndMap(string, stringLength);
  390.     HashEntryRec*    entry = fTable + tableIndex;
  391.     ODBoolean        result = kODFalse;
  392.     
  393.     if (stringLength == 0)
  394.         THROW(kODErrInvalidKey);
  395.  
  396.     if (entry->node != kODNULL)
  397.     {
  398.         LinkedNodesIterator    iter(this, tableIndex);
  399.         EntryNode*            someNode;
  400.  
  401.         for (someNode = iter.First();
  402.                 iter.IsNotComplete();
  403.                 someNode = iter.Next())
  404.         {
  405.             // match
  406.             if ((someNode->keySize == stringLength)
  407.                 && (!memcmp((const char*)someNode->key, (const char*)string,
  408.                             (size_t)stringLength)))
  409.             {
  410.                 *value = someNode->value;
  411.                 *valueLength = someNode->valueLen;
  412.                 result = kODTrue;
  413.                 break;
  414.             }
  415.         }
  416.     }
  417.  
  418.     return result;
  419. }
  420.  
  421. //------------------------------------------------------------------------------
  422. // StringHashTable::Exists
  423. //
  424. //    Hash the key, Map the hash to the range of table index values, search the
  425. //    the linked list of EntryNodes for the key. Signal error if not found.
  426. //------------------------------------------------------------------------------
  427.  
  428. ODBoolean StringHashTable::Exists(ODUByte* string, ODULong stringLength)
  429. {
  430.     ODULong        tableIndex = this->HashAndMap(string, stringLength);
  431.     HashEntryRec*    entry = fTable + tableIndex;
  432.     ODBoolean        result = kODFalse;
  433.     
  434.     //if (stringLength == 0)
  435.         //THROW(kODErrInvalidKey);
  436.  
  437.     if (stringLength != 0)
  438.     {
  439.         if (entry->node != kODNULL)
  440.         {
  441.             LinkedNodesIterator    iter(this, tableIndex);
  442.             EntryNode*            someNode;
  443.     
  444.             for (someNode = iter.First();
  445.                     iter.IsNotComplete();
  446.                     someNode = iter.Next())
  447.             {
  448.                 // match
  449.                 if ((someNode->keySize == stringLength)
  450.                     && (!memcmp((const char*)someNode->key, (const char*)string,
  451.                                 (size_t)stringLength)))
  452.                 {
  453.                     result = kODTrue;
  454.                     break;
  455.                 }
  456.             }
  457.         }
  458.     }
  459.  
  460.     return result;
  461. }
  462.  
  463. //------------------------------------------------------------------------------
  464. // StringHashTable::StdHash
  465. //
  466. //    Adds all the characters in the string and returns the smallest and largest
  467. //    numbers that they could add up to in hashValueLowerBounds and
  468. //    hashValueUpperBounds.
  469. //------------------------------------------------------------------------------
  470.  
  471. ODULong StringHashTable::StdHash(ODUByte* string,
  472.                                             ODULong stringLength,
  473.                                             ODULong& hashValueLowerBounds,
  474.                                             ODULong& hashValueUpperBounds)
  475. {
  476.     ODULong    result = 0;
  477.  
  478.     for (ODULong i = 0; i < stringLength; i++)
  479.     {
  480.         result += *(string + i);
  481.     }
  482.     hashValueLowerBounds = 0;
  483.     hashValueUpperBounds = 255 * stringLength;
  484.     return result;
  485. }
  486.  
  487. //------------------------------------------------------------------------------
  488. // StringHashTable::MapToTableIndex
  489. //
  490. //    Map hash value in its range to the range 0..(fNumSlots - 1).
  491. //------------------------------------------------------------------------------
  492.  
  493. ODULong StringHashTable::MapToTableIndex(ODULong hash,
  494.                                                 ODULong hashValueLowerBounds,
  495.                                                 ODULong hashValueUpperBounds)
  496. {
  497.     if (hashValueLowerBounds >= hashValueUpperBounds)
  498.         return 0;
  499.     else
  500.     {
  501.         double index = (double)(hash - hashValueLowerBounds)
  502.             / (double)(hashValueUpperBounds - hashValueLowerBounds)
  503.             * (double)(fNumSlots - 1);
  504.         double indexToRound = index + .5;
  505.         return (ODULong)indexToRound;
  506.     }
  507. }
  508.  
  509. //------------------------------------------------------------------------------
  510. // StringHashTable::HashAndMap
  511. //
  512. //    Utility function.
  513. //------------------------------------------------------------------------------
  514.  
  515. ODULong StringHashTable::HashAndMap(ODUByte* string, ODULong stringLength)
  516. {
  517.     ODULong    hashUpperBounds, hashLowerBounds;
  518.  
  519.     ODULong hash = fHashFunc(string, stringLength, hashLowerBounds,
  520.                                 hashUpperBounds);
  521.     return this->MapToTableIndex(hash, hashLowerBounds, hashUpperBounds);
  522. }
  523.  
  524. //------------------------------------------------------------------------------
  525. // StringHashTable::PrintTable
  526. //
  527. //    Testing / Debugging function.
  528. //------------------------------------------------------------------------------
  529.  
  530. #include <stdio.h>
  531.  
  532. void StringHashTable::PrintTable(FILE* outfile)
  533. {
  534.     StringHashTableIterator    iter(this);
  535.     ODUByte*                    string;
  536.     ODULong                    len;
  537.     ODPtr                        value;
  538.     ODULong                    valueLen;
  539.  
  540.     fprintf(outfile, "The table contains this:\n");
  541.  
  542.     for (iter.First(&string, &len, &value, &valueLen);
  543.             iter.IsNotComplete();
  544.             iter.Next(&string, &len, &value, &valueLen))
  545.     {    
  546.         fprintf(outfile, "%s, %d%d\n", string, value, valueLen);
  547.     }
  548.  
  549.     fprintf(outfile, "\n");
  550. }
  551.  
  552. //------------------------------------------------------------------------------
  553. // StringHashTable::PrintDistribution
  554. //
  555. //    Testing / Debugging function.
  556. //------------------------------------------------------------------------------
  557.  
  558. void StringHashTable::PrintDistribution(FILE* outfile)
  559. {
  560.     fprintf(outfile, "The table looks like this:\n");
  561.  
  562.     for (ODULong i = 0; i < fNumSlots; i++)
  563.     {
  564.         fprintf(outfile, "Slot %d:\n", i);
  565.  
  566.         LinkedNodesIterator    iter(this, i);
  567.         EntryNode*            someNode;
  568.  
  569.         for (someNode = iter.First();
  570.                 iter.IsNotComplete();
  571.                 someNode = iter.Next())
  572.         {
  573.             fprintf(outfile, "\t%s\n", someNode->key);
  574.         }
  575.     }
  576.  
  577.     fprintf(outfile, "\n");
  578. }
  579.  
  580. //==============================================================================
  581. // StringHashTableIterator
  582. //==============================================================================
  583.  
  584. //------------------------------------------------------------------------------
  585. // StringHashTableIterator::StringHashTableIterator
  586. //------------------------------------------------------------------------------
  587.  
  588. StringHashTableIterator::StringHashTableIterator(StringHashTable* tb)
  589. {
  590.     fTable = tb;
  591.     fTableIndex = 0;
  592.     fCurNode = kODNULL;
  593.     fDone = kODFalse;
  594. }
  595.  
  596. //------------------------------------------------------------------------------
  597. // StringHashTableIterator::First
  598. //
  599. //    Use GetNext to find first. Set flag if we don't find an entry.
  600. //------------------------------------------------------------------------------
  601.  
  602. void StringHashTableIterator::First(ODUByte** string, ODULong* len,
  603.                                             ODPtr* value,
  604.                                             ODULong* valueLength)
  605. {
  606.     if (!this->GetNext(string, len, value, valueLength))
  607.     {
  608. //        *len  = 0;
  609. //        *string = (ODUByte*)kODNULL;
  610.         fDone = kODTrue;
  611.     }
  612. }
  613.  
  614. //------------------------------------------------------------------------------
  615. // StringHashTableIterator::Next
  616. //
  617. //    Use GetNext. Set flag if we don't find an entry.
  618. //------------------------------------------------------------------------------
  619.  
  620. void StringHashTableIterator::Next(ODUByte** string, ODULong* len,
  621.                                             ODPtr* value,
  622.                                             ODULong* valueLength)
  623. {
  624.     if (!this->GetNext(string, len, value, valueLength))
  625.     {
  626. //        *len  = 0;
  627. //        *string = (ODUByte*)kODNULL;
  628.         fDone = kODTrue;
  629.     }
  630. }
  631.  
  632. //------------------------------------------------------------------------------
  633. // StringHashTableIterator::GetNext
  634. //
  635. // kODTrue is returned if an entry was found, kODFalse otherwise. The
  636. //    values pointed to by the parameters are undefined if kODFalse is
  637. //    returned.
  638. //    We can record the index of the last hash bucket we were looking in, but we
  639. //    can't record the index into the linked list at that bucket. So we keep a
  640. //    pointer to the last node found and look for it the next time we iterate
  641. //    through that list.
  642. //------------------------------------------------------------------------------
  643.  
  644. ODBoolean StringHashTableIterator::GetNext(ODUByte** string, ODULong* len,
  645.                                                 ODPtr* value,
  646.                                                 ODULong* valueLength)
  647. {
  648.     for (ODULong i = fTableIndex; i < fTable->GetNumSlots(); i++)
  649.     {
  650.         LinkedNodesIterator    iter(fTable, i);
  651.         EntryNode*            someNode;
  652.         ODBoolean            pastLastNode = fCurNode ? kODFalse : kODTrue;
  653.  
  654.         for (someNode = iter.First();
  655.                 iter.IsNotComplete();
  656.                 someNode = iter.Next())
  657.         {
  658.             if ((i == fTableIndex) && (someNode == fCurNode))
  659.             {
  660.                 pastLastNode = kODTrue;
  661.                 continue;
  662.             }
  663.             if (pastLastNode || (i != fTableIndex))
  664.             {
  665.                 fTableIndex = i;
  666.                 fCurNode = someNode;
  667.                 *string = someNode->key;
  668.                 *len = someNode->keySize;
  669.                 *value = someNode->value;
  670.                 *valueLength = someNode->valueLen;
  671.                 return kODTrue;
  672.             }
  673.         }
  674.     }
  675.     return kODFalse;
  676. }
  677.  
  678.